package controllers

import (
	"encoding/json"
	"fmt"
	"strings"

	"cvevulner/common"
	"cvevulner/errcode"
	"cvevulner/models"
	"cvevulner/task"
	"cvevulner/util"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
)

type UserUploadController struct {
	beego.Controller
}

type ResultData struct {
	CveNum string `json:"CveNum"`
	Status int    `json:"Status"`
}

func (c *UserUploadController) RetData(resp map[string]interface{}, list []string) {
	c.Data["json"] = resp
	c.ServeJSON()
	// sysnc cve and create issue
	if list != nil && len(list) > 0 {
		go func() {
			synErr := task.SyncCveAndIssue(list)
			if synErr != nil {
				logs.Error("SyncCveAndIssue, Sync cve data error, err: ", synErr)
			} else {
				logs.Info("SyncCveAndIssue, cve data has been synchronized")
			}
			return
		}()
	}
}

func (c *CveErrorFeedBackController) RetData(resp map[string]interface{}) {
	c.Data["json"] = resp
	c.ServeJSON()
}

type CveErrorFeedBackController struct {
	beego.Controller
}

type CveErrorData struct {
	CveNum        string `json:"cveNum"`
	ErrorDesc     string `json:"errorDesc"`
	CveUploadTime string `json:"cveUploadTime"`
}

// @Title Get cveerrorfeedback
// @Description get cveerrorfeedback
// @Param	startDate		endDate 	string	false
// @Success 200 {object} models.uploadcve
// @Failure 403 :endDate is err
// @router / [get]
func (u *CveErrorFeedBackController) Get() {
	req := u.Ctx.Request
	addr := req.RemoteAddr
	logs.Info("Method: ", req.Method, "Client request ip address: ", addr,
		",Header: ", req.Header, ",body: ", req.Body)
	resp := make(map[string]interface{})
	var ced []CveErrorData
	resp["errno"] = errcode.RecodeUnknowErr
	resp["errmsg"] = errcode.RecodeText(errcode.RecodeUnknowErr)
	resp["body"] = []CveErrorData{}
	//defer u.RetData(resp)
	var iw models.IpWhite
	if addr != "" {
		addrIp := strings.Split(addr, ":")
		err := models.GetIpWhite(addrIp[0], &iw)
		if err != nil {
			resp["errno"] = errcode.RecodeIpErr
			resp["errmsg"] = errcode.RecodeText(errcode.RecodeIpErr)
			u.RetData(resp)
			return
		}
	} else {
		resp["errno"] = errcode.RecodeIpErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeIpErr)
		u.RetData(resp)
		return
	}
	token := u.GetString("token")
	if token == "" {
		resp["errno"] = errcode.RecodeSessionErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeSessionErr)
		u.RetData(resp)
		return
	} else {
		ok := models.CheckToken(token)
		if !ok {
			resp["errno"] = errcode.RecodeRoleErr
			resp["errmsg"] = errcode.RecodeText(errcode.RecodeRoleErr)
			u.RetData(resp)
			return
		}
	}
	startDate := u.GetString("startDate")
	if startDate != "" {
		logs.Info("Query start date: ", startDate)
	}

	endDate := u.GetString("endDate")
	if endDate != "" {
		logs.Info("Query end date: ", endDate)
	} else {
		endDate = common.GetCurTime()
	}

	it, ok := models.QueryCveErrorInfo(3, startDate, endDate)
	if ok {
		for _, cveErr := range it {
			var ved CveErrorData
			ved.CveNum = cveErr.CveNum
			ved.CveUploadTime = common.TimeConverStr(cveErr.CreateTime.String()[:19])
			ved.ErrorDesc = cveErr.ErrorDescription
			ced = append(ced, ved)
		}
		resp["body"] = ced
		resp["errno"] = errcode.RecodeOk
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeOk)
		u.RetData(resp)
	} else {
		resp["errno"] = errcode.RecodeNodata
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeNodata)
		u.RetData(resp)
		return
	}
}

// @Title UserUpload
// @Description UserUpload
// @Param	body		body 	models.OriginUpstream	true		"body for user content"
// @Success 200 {int} models.OriginUpstream.CveId
// @Failure 403 body is empty
// @router / [post]
func (u *UserUploadController) Post() {
	var uploaddata common.UploadData
	var ResDataList []ResultData
	req := u.Ctx.Request
	addr := req.RemoteAddr
	logs.Info("Method: ", req.Method, "Client request ip address: ", addr, ",Header: ", req.Header)
	resp := make(map[string]interface{})
	resp["errno"] = errcode.RecodeUnknowErr
	resp["errmsg"] = errcode.RecodeText(errcode.RecodeUnknowErr)
	resp["body"] = []ResultData{}
	//defer u.RetData(resp)
	json.Unmarshal(u.Ctx.Input.RequestBody, &uploaddata)
	logs.Info("Cve upload request parameters: ", string(u.Ctx.Input.RequestBody))
	var iw models.IpWhite
	if addr != "" {
		addrIp := strings.Split(addr, ":")
		err := models.GetIpWhite(addrIp[0], &iw)
		if err != nil {
			resp["errno"] = errcode.RecodeIpErr
			resp["errmsg"] = errcode.RecodeText(errcode.RecodeIpErr)
			u.RetData(resp, nil)
			return
		}
	} else {
		resp["errno"] = errcode.RecodeIpErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeIpErr)
		u.RetData(resp, nil)
		return
	}
	//Judge whether it is legal
	if uploaddata.Token == "" {
		resp["errno"] = errcode.RecodeSessionErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeSessionErr)
		resp["body"] = []ResultData{}
		logs.Error("token request parameter is empty!")
		u.RetData(resp, nil)
		return
	} else {
		// Check token
		ok := models.CheckToken(uploaddata.Token)
		if !ok {
			resp["errno"] = errcode.RecodeSessionErr
			resp["errmsg"] = errcode.RecodeText(errcode.RecodeSessionErr)
			resp["body"] = []ResultData{}
			logs.Error("token verification failed!")
			u.RetData(resp, nil)
			return
		}
	}
	if uploaddata.CveData == nil || len(uploaddata.CveData) == 0 {
		resp["errno"] = errcode.RecodeNodata
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeNodata)
		resp["body"] = []ResultData{}
		logs.Error("cve data is empty")
		u.RetData(resp, nil)
		return
	}
	for _, CveDataDict := range uploaddata.CveData {
		resDataList := AddOrgUpstream(uploaddata.Source, CveDataDict)
		if len(resDataList) > 0 {
			ResDataList = append(ResDataList, resDataList...)
		}
	}
	resp["errno"] = errcode.RecodeOk
	resp["errmsg"] = errcode.RecodeText(errcode.RecodeOk)
	resp["body"] = ResDataList
	var list = make([]string, 0)
	for _, v := range ResDataList {
		if v.Status == 0 {
			list = append(list, v.CveNum)
		}
	}
	u.RetData(resp, list)
	return
}

func AddOrgUpstream(source int, CveDataDict common.CveOriginData) (ResDataList []ResultData) {
	defer common.Catchs()
	logs.Info("Each request parameter: ", CveDataDict)
	// Record data flow
	AddOrgUpstreamRecord(source, CveDataDict)
	var ResData ResultData
	ids := CveDataDict.Ids
	if len(ids) < 1 {
		ResData.CveNum = ids
		ResData.Status = 1
		ResDataList = append(ResDataList, ResData)
		logs.Error("ids is null, cveDataDict:", CveDataDict)
		return
	}
	cveNum := CveDataDict.CveNum
	if len(cveNum) < 2 {
		ResData.CveNum = cveNum
		ResData.Status = 1
		ResDataList = append(ResDataList, ResData)
		logs.Error("CveNum is null, cveDataDict:", CveDataDict)
		return
	}
	updateType := CveDataDict.UpdateType
	cvePackName := ""
	if len(CveDataDict.CvePackName) > 0 {
		cvePackName = strings.Join(CveDataDict.CvePackName, ",")
	}
	packName := ""
	if len(CveDataDict.PackName) > 0 {
		packName = strings.Join(CveDataDict.PackName, ",")
	} else {
		ResData.CveNum = cveNum
		ResData.Status = 1
		ResDataList = append(ResDataList, ResData)
		logs.Error("PackName is null, cveDataDict:", CveDataDict)
		return
	}
	title := CveDataDict.Title
	affectProduct := ""
	if len(CveDataDict.AffectProduct) > 0 {
		affectProduct = strings.Join(CveDataDict.AffectProduct, ",")
	} else {
		affectProduct = packName
	}
	cnnvdID := CveDataDict.CnnvdID
	cnvdID := CveDataDict.CnvdID
	publishedDate := CveDataDict.PublishedDate
	vulStatus := CveDataDict.VulStatus
	version := CveDataDict.Version
	if version == "" {
		version = packName
	}
	var orCve models.OriginUpstream
	if ids != "" {
		ids = common.DeletePreAndSufSpace(ids)
	}
	orCve.Ids = ids
	if cveNum != "" {
		cveNum = common.DeletePreAndSufSpace(cveNum)
	}
	orCve.Source = source
	orCve.CveNum = cveNum
	orCve.Version = version
	orCve.UpdateType = updateType
	orCve.CvePackName = cvePackName
	orCve.Credibility = CveDataDict.Credibility
	//if CveDataDict.Credibility == 7 {
	//	rejectIssue(cveNum)
	//}
	if packName != "" {
		packName = common.DeletePreAndSufSpace(packName)
	}
	orCve.PackName = packName
	orCve.Title = title
	if affectProduct == "" {
		orCve.AffectProduct = packName
	}
	orCve.CnnvdID = cnnvdID
	orCve.CnvdID = cnvdID
	orCve.IsExit = 0
	orCve.PublishedDate = publishedDate
	orCve.FirstPerTime = CveDataDict.GetTime
	orCve.FirstGetTime = CveDataDict.EndGetTime
	checkPackageAndVersion(packName, &orCve)
	orCve.VulStatus = vulStatus
	if strings.ToLower(updateType) == "delete" {
		orCve.Status = 3
	} else if strings.ToLower(updateType) == "update" {
		orCve.Status = 1
	} else {
		orCve.Status = 0
	}
	orCve.CreateTime = common.GetCurTime()
	orCve.UpdateTime = common.GetCurTime()
	var od models.OriginUpstreamDesc
	od.EnDescription = CveDataDict.Description.EnDesc
	od.ZhDescription = CveDataDict.Description.ZhDesc
	var ous models.OriginUpstreamConfig
	ous.Nodes = " "
	var osi models.OriginUpstreamImpact
	osi.Impact = " "
	var osp models.OriginUpstreamPoc
	osp.Url = CveDataDict.Poc.Url
	osp.Date = CveDataDict.Poc.Date
	osp.Dbindex = CveDataDict.Poc.Dbindex
	osp.Desc = CveDataDict.Poc.Desc
	osp.Path = CveDataDict.Poc.Path
	osp.Source = CveDataDict.Poc.Source
	var ose []*models.OriginUpstreamEvent
	if len(CveDataDict.Event) > 0 {
		for _, v := range CveDataDict.Event {
			var event models.OriginUpstreamEvent
			event.Title = v.Title
			event.Url = v.Url
			event.Date = v.Date
			event.Description = v.Description
			ose = append(ose, &event)
		}
	}
	var osv []*models.OriginUpstreamVulType
	if len(CveDataDict.VulType) > 0 {
		for _, v := range CveDataDict.VulType {
			var typ models.OriginUpstreamVulType
			typ.Cwe = v.Cwe
			typ.ZhDesc = v.Zh
			typ.EnDesc = v.En
			osv = append(osv, &typ)
		}
	}
	var osf models.OriginUpstreamFixSuggest
	osf.Detail = CveDataDict.FixSuggest.Detail
	dbCve, ok := models.QueryCveOriginByIds(ids)
	if ok {
		if orCve.Status != 3 {
			orCve.Status = 1
		}
		orCve.UpdateTime = common.GetCurTime()
		if orCve.Status == 3 {
			orCve.DeleteTime = common.GetCurTime()
		}
		logs.Info("The currently inserted data already exists: ", dbCve)
	}

	var packageUrls []*models.OriginUpstreamPackageUrl

	if source == 0 {
		p := CveDataDict.PackageUrl
		for _, i := range p.GO {
			packageUrls = append(packageUrls, &models.OriginUpstreamPackageUrl{
				Purl: i.Purl, VersionStartIncluding: i.VersionStartIncluding,
				VersionStartExcluding: i.VersionStartExcluding, VersionEndExcluding: i.VersionEndExcluding,
				VersionEndIncluding: i.VersionEndIncluding, Source: common.GO,
			})
		}

		for _, i := range p.Huntr {
			packageUrls = append(packageUrls, &models.OriginUpstreamPackageUrl{
				Purl: i.Purl, VersionStartIncluding: i.VersionStartIncluding,
				VersionStartExcluding: i.VersionStartExcluding, VersionEndExcluding: i.VersionEndExcluding,
				VersionEndIncluding: i.VersionEndIncluding, Source: common.Huntr,
			})
		}

		for _, i := range p.Gemnasium {
			packageUrls = append(packageUrls, &models.OriginUpstreamPackageUrl{
				Purl: i.Purl, VersionStartIncluding: i.VersionStartIncluding,
				VersionStartExcluding: i.VersionStartExcluding, VersionEndExcluding: i.VersionEndExcluding,
				VersionEndIncluding: i.VersionEndIncluding, Source: common.Gemnasium,
			})
		}

		for _, i := range p.GithubAdvistory {
			packageUrls = append(packageUrls, &models.OriginUpstreamPackageUrl{
				Purl: i.Purl, VersionStartIncluding: i.VersionStartIncluding,
				VersionStartExcluding: i.VersionStartExcluding, VersionEndExcluding: i.VersionEndExcluding,
				VersionEndIncluding: i.VersionEndIncluding, Source: common.GithubAdvistory,
			})
		}
	}

	if CveDataDict.AffectUpdate {
		orCve.AffectUpdate = 1
	}

	_, err := models.CreateOriginCve(CveDataDict, &orCve, &od, &ous, &osi, &osp, ose, osv, &osf, packageUrls)
	if err == nil {
		logs.Info("Cve original data is successfully created CveNum: ", CveDataDict.Ids)
		ResData.CveNum = CveDataDict.Ids
		ResData.Status = 0
		ResDataList = append(ResDataList, ResData)
	} else {
		logs.Info("cve creation failed CveNum: ", CveDataDict.Ids)
		ResData.CveNum = CveDataDict.Ids
		ResData.Status = 1
		ResDataList = append(ResDataList, ResData)
	}
	return
}

func checkPackageAndVersion(packName string, orCve *models.OriginUpstream) {
	packNameList := []string{}
	if packName != "" && len(packName) > 0 {
		packNameList = strings.Split(packName, ",")
	}
	if len(packNameList) > 0 {
		for _, pk := range packNameList {
			pkList := strings.Split(pk, "==")
			if len(pkList) == 2 {
				var gits models.GitOpenEuler
				gits.PackageName = pkList[0]
				gits.Version = pkList[1]
				gits.Status = 1
				ok := models.QueryCveOpeneulerdata(&gits)
				if ok {
					orCve.IsExit = 1
					logs.Info("The data corresponding to src-openEuler is: ", gits)
					break
				} else {
					opy := models.OpenGussYaml{PackageName: pkList[0], Version: pkList[1]}
					openErr := models.GetOpengaussYaml(&opy, "PackageName", "Version")
					if openErr == nil && opy.Id > 0 {
						orCve.IsExit = 1
						break
					}
				}
			}
		}
	}
}

func AddOrgUpstreamRecord(source int, CveDataDict common.CveOriginData) {
	orCve := models.OriginUpstreamRecord{}
	ids := CveDataDict.Ids
	cveNum := CveDataDict.CveNum
	updateType := CveDataDict.UpdateType
	cvePackName := ""
	if len(CveDataDict.CvePackName) > 0 {
		cvePackName = strings.Join(CveDataDict.CvePackName, ",")
	}
	orCve.Source = source
	packName := ""
	if len(CveDataDict.PackName) > 0 {
		packName = strings.Join(CveDataDict.PackName, ",")
	}
	title := CveDataDict.Title
	affectProduct := ""
	if len(CveDataDict.AffectProduct) > 0 {
		affectProduct = strings.Join(CveDataDict.AffectProduct, ",")
	}
	cnnvdID := CveDataDict.CnnvdID
	cnvdID := CveDataDict.CnvdID
	publishedDate := CveDataDict.PublishedDate
	vulStatus := CveDataDict.VulStatus
	version := CveDataDict.Version
	orCve.Ids = ids
	if cveNum != "" {
		cveNum = common.DeletePreAndSufSpace(cveNum)
	}
	orCve.CveNum = cveNum
	orCve.Version = version
	orCve.UpdateType = updateType
	orCve.CvePackName = cvePackName
	orCve.Credibility = CveDataDict.Credibility
	if packName != "" {
		packName = common.DeletePreAndSufSpace(packName)
	}
	orCve.PackName = packName
	orCve.Title = title
	if affectProduct == "" {
		orCve.AffectProduct = packName
	}
	orCve.CnnvdID = cnnvdID
	orCve.CnvdID = cnvdID
	orCve.IsExit = 0
	orCve.PublishedDate = publishedDate
	orCve.FirstPerTime = CveDataDict.GetTime
	orCve.FirstGetTime = CveDataDict.EndGetTime
	orCve.VulStatus = vulStatus
	if strings.ToLower(updateType) == "delete" {
		orCve.Status = 3
	} else if strings.ToLower(updateType) == "update" {
		orCve.Status = 1
	} else {
		orCve.Status = 0
	}
	orCve.CreateTime = common.GetCurTime()

	if CveDataDict.AffectUpdate {
		orCve.AffectUpdate = 1
	}

	err := models.InsertOriginCveRecord(&orCve)
	if err != nil {
		logs.Error("InsertOriginCveRecord, err: ", err)
	}
	// Get the date one month ago
	beforeDate := common.GetBeforeDate(1, -90)
	models.DeleteOriginCveRecord(beforeDate)
}

func rejectIssue(cve string) {
	tokenList := models.QueryAuthTokenInfo()
	tokenMap := make(map[int8]models.AuthTokenInfo, len(tokenList))
	if len(tokenList) > 0 {
		for _, tl := range tokenList {
			tokenMap[tl.OrganizationID] = tl
		}
	}

	res, err := models.GetCveVulnCenter(cve)
	if err != nil || len(res) == 0 {
		return
	}

	for _, vl := range res {
		if vl.OrganizationID != 1 {
			continue
		}
		its := models.IssueTemplate{CveId: vl.CveId, CveNum: vl.CveNum}
		tmpErr := models.GetIssueTemplateByColName(&its, "CveId", "CveNum")
		if its.TemplateId == 0 || tmpErr != nil || its.Status == 4 {
			continue
		}
		authToken := tokenMap[vl.OrganizationID]
		err = setReject(authToken.EnId, its.IssueId, authToken.AccessToken)
		if err != nil {
			logs.Error("UpdateEntIssueDetail, Update issue failed, err: ", err, ", issueId: ", its.IssueId)
			continue
		}
		its.Status = 4
		its.StatusName = "rejected"
		_ = models.UpdateIssueTemplate(&its, "Status", "StatusName")
	}
}

func setReject(enterpriseId, issueId int64, token string) error {
	url := fmt.Sprintf("https://api.gitee.com/enterprises/%v/issues/%v",
		enterpriseId, issueId)
	requestBody := fmt.Sprintf(`{"access_token": "%s","issue_state_id": "437578"}`, token)
	_, err := util.HTTPPutMap(url, requestBody)
	if err != nil {
		return err
	}
	return nil
}
